/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.editor.ext; import java.lang.reflect.Modifier; import java.util.Iterator; import java.util.Comparator; import java.util.List; import java.util.ArrayList; import java.util.Arrays; import java.util.Map; import java.util.HashMap; import java.util.TreeMap; import java.util.TreeSet; /** * Java completion support finder * * @author Miloslav Metelka * @version 1.00 */ public class JCBaseFinder extends JCompletion.AbstractProvider implements JCFinder { static final Comparator CLASS_NAME_COMPARATOR = new DefaultClassNameComparator(); private static final int PACKAGE_PRE_ALLOC = 1009; private static final int CLASS_PRE_ALLOC = 5003; protected JCPackage[] allPackages; protected JCClass[] allClassesByName; /** * @associates JCPackage */ protected HashMap allPackagesMap = new HashMap(PACKAGE_PRE_ALLOC); /** * @associates JCClass */ protected HashMap allClassesMap = new HashMap(CLASS_PRE_ALLOC); public Iterator getClasses() { return new ArrayList(Arrays.asList(getAllClasses())).iterator(); } public boolean append(JCClassProvider cp) { if (!super.append(cp)) { return false; } return true; } protected boolean appendClass(JCClass cls) { if (!cheapUpdate(cls)) { // try cheap update first invalidate(); // reset all if failed } return true; } public void reset() { allClassesMap.clear(); invalidate(); } protected void invalidate() { allPackagesMap.clear(); allPackages = null; allClassesByName = null; } public JCPackage getExactPackage(String packageName) { if (allPackages == null) { // not initialized yet build(); } return (JCPackage)allPackagesMap.get(packageName); } public JCClass getExactClass(String classFullName) { return (JCClass)allClassesMap.get(classFullName); } protected JCPackage[] getAllPackages() { if (allPackages == null) { build(); } return allPackages; } protected JCClass[] getAllClassesByName() { if (allClassesByName == null) { build(); } return allClassesByName; } protected JCClass[] getAllClasses() { JCClass[] allClasses = (JCClass[])getAllClassesByName().clone(); Arrays.sort(allClasses); return allClasses; } private boolean cheapUpdate(JCClass cls) { Object o = allClassesMap.put(cls.getFullName(), cls); if (allClassesByName != null && o != null) { // inited and class already there, can do cheap update String pkgName = cls.getPackageName(); JCPackage pkg = (JCPackage)allPackagesMap.get(pkgName); if (pkg == null) { // strange - package missing in package map return false; } JCClass[] clist = pkg.getClasses(); int ind = Arrays.binarySearch(clist, cls); if (ind < 0) { // strange - class is missing in the package class list return false; } clist[ind] = cls; // Update allClassesByName array - can be more with the same name ind = Arrays.binarySearch(allClassesByName, cls, CLASS_NAME_COMPARATOR); // adjust start index if (ind < 0) { // not exact match ind = -ind - 1; } // position to start of matching classes String name = cls.getName(); while (ind >= 0 && ind < allClassesByName.length) { if (!allClassesByName[ind].getName().startsWith(name)) { break; } ind--; } ind++; // replace the matching class in the list boolean updated = false; while (ind < allClassesByName.length) { if (cls.equals(allClassesByName[ind])) { allClassesByName[ind] = cls; updated = true; break; } if (!name.equals(allClassesByName[ind].getName())) { break; } ind++; } return updated; } return false; } private void addPackage(JCPackage pkg, boolean force) { if (force || !allPackagesMap.containsKey(pkg)) { allPackagesMap.put(pkg.getName(), pkg); String name = pkg.getName(); int i = name.lastIndexOf('.'); if (i >= 0) { addPackage(new JCompletion.BasePackage(name.substring(0, i)), false); } } } protected void build() { // Build class array and class by name array JCClass[] allClasses = new JCClass[allClassesMap.size()]; allClassesByName = new JCClass[allClasses.length]; Iterator itr = allClassesMap.values().iterator(); int ind = 0; while (itr.hasNext()) { allClasses[ind] = (JCClass)itr.next(); allClassesByName[ind] = allClasses[ind]; ind++; } Arrays.sort(allClasses); Arrays.sort(allClassesByName, CLASS_NAME_COMPARATOR); // Build package array allPackagesMap.clear(); allPackages = JCompletion.EMPTY_PACKAGES; if (allClasses.length > 0) { ArrayList pkgClassList = new ArrayList(); JCPackage curPkg = new JCompletion.BasePackage(allClasses[0].getPackageName()); for (int i = 0; i < allClasses.length; i++) { String pkgName = allClasses[i].getPackageName(); if (!curPkg.equals(pkgName)) { JCClass classes[] = new JCClass[pkgClassList.size()]; pkgClassList.toArray(classes); curPkg.setClasses(classes); pkgClassList.clear(); addPackage(curPkg, true); curPkg = new JCompletion.BasePackage(pkgName); } pkgClassList.add(allClasses[i]); } JCClass classes[] = new JCClass[pkgClassList.size()]; pkgClassList.toArray(classes); curPkg.setClasses(classes); addPackage(curPkg, true); allPackages = new JCPackage[allPackagesMap.size()]; itr = allPackagesMap.values().iterator(); ind = 0; while (itr.hasNext()) { allPackages[ind] = (JCPackage)itr.next(); ind++; } } Arrays.sort(allPackages); } public List findPackages(String name, boolean exactMatch, boolean subPackages) { List ret = new ArrayList(); if (exactMatch) { JCPackage pkg = getExactPackage(name); if (pkg != null) { ret.add(pkg); } if (!subPackages) { return ret; } } JCPackage packages[] = getAllPackages(); JCPackage key = new JCompletion.BasePackage(name); int ind = Arrays.binarySearch(packages, key); int nameLen = name.length(); // adjust start index if (ind < 0) { // not exact match ind = -ind - 1; } // position to start of matching package names while (ind >= 0 && ind < packages.length) { if (!packages[ind].getName().startsWith(name)) { break; } ind--; } ind++; // add the matching packages to the list int reqDotCount = key.getDotCount(); while (ind < packages.length) { String pkgName = packages[ind].getName(); if (!pkgName.startsWith(name)) { break; } if (exactMatch ? (pkgName.length() > nameLen && pkgName.charAt(nameLen) == '.') : (subPackages || packages[ind].getDotCount() == reqDotCount) ) { ret.add(packages[ind]); } ind++; } return ret; } /** Find classes by name and possibly in some package * @param pkg package where the classes should be searched for. It can be null * @param begining of the name of the class. The package name must be omitted. * @param exactMatch whether the given name is the exact requested name * of the class or not. * @return list of the matching classes */ public List findClasses(JCPackage pkg, String name, boolean exactMatch) { List ret = new ArrayList(); JCClass[] classes; int ind; JCClass key = new JCompletion.SimpleClass(name, ""); // NOI18N int nameLen = name.length(); if (pkg != null) { classes = pkg.getClasses(); } else { // pkg is null classes = getAllClassesByName(); } ind = Arrays.binarySearch(classes, key, CLASS_NAME_COMPARATOR); // adjust start index if (ind < 0) { // not exact match ind = -ind - 1; } // position to start of matching classes while (ind >= 0 && ind < classes.length) { if (!classes[ind].getName().startsWith(name)) { break; } ind--; } ind++; // add the matching classes to the list while (ind < classes.length) { String className = classes[ind].getName(); if (!className.startsWith(name)) { break; } if (!exactMatch || className.length() == nameLen) { ret.add(classes[ind]); } ind++; } return ret; } /** Get non-static outer classes to search * the fields and methods there. The original class is added * as the first member of the resulting list. */ private List getOuterClasses(JCClass cls) { JCFinder finder = JCompletion.getFinder(); ArrayList outers = new ArrayList(); outers.add(cls); int lastDotInd = cls.getName().lastIndexOf('.'); while ((cls.getModifiers() & Modifier.STATIC) == 0 && lastDotInd >= 0) { int pkgLen = cls.getPackageName().length(); String fullName = cls.getFullName().substring(0, ((pkgLen > 0) ? (pkgLen + 1) : 0) + lastDotInd); cls = finder.getExactClass(fullName); if (cls != null) { outers.add(cls); lastDotInd = cls.getName().lastIndexOf('.'); } else { break; } } return outers; } /** Find fields by name in a given class. * @param cls class which is searched for the fields. * @param name start of the name of the field * @param exactMatch whether the given name of the field is exact * @param staticOnly whether search for the static fields only * @return list of the matching fields */ public List findFields(JCClass cls, String name, boolean exactMatch, boolean staticOnly, boolean inspectOuterClasses) { TreeSet ts = new TreeSet(); List clsList = getClassList(cls); String pkgName = cls.getPackageName(); for (int i = clsList.size() - 1; i >= 0; i--) { cls = getExactClass(((JCClass)clsList.get(i)).getFullName()); if (cls != null) { boolean difPkg = !cls.getPackageName().equals(pkgName); List outerList = (i == 0 && inspectOuterClasses && cls.getName().indexOf('.') >= 0) ? getOuterClasses(cls) : null; int outerInd = (outerList != null) ? (outerList.size() - 1) : -1; do { if (outerInd >= 0) { cls = (JCClass)outerList.get(outerInd--); } JCField[] fields = cls.getFields(); for (int j = 0; j < fields.length; j++) { JCField fld = fields[j]; int mods = fld.getModifiers(); if ((staticOnly && (mods & Modifier.STATIC) == 0) || (i > 0 && (mods & Modifier.PRIVATE) != 0) || (difPkg && (mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0) ) { continue; } if (exactMatch) { if (!fld.getName().equals(name)) { continue; } } else { if (!fld.getName().startsWith(name)) { continue; } } ts.add(fld); } } while (outerInd >= 0); } } return new ArrayList(ts); } /** Find methods by name in a given class. * @param cls class which is searched for the methods. * @param name start of the name of the method * @param exactMatch whether the given name of the method is exact * @param staticOnly whether search for the static methods only * @return list of the matching methods */ public List findMethods(JCClass cls, String name, boolean exactMatch, boolean staticOnly, boolean inspectOuterClasses) { TreeSet ts = new TreeSet(); List clsList = getClassList(cls); String pkgName = cls.getPackageName(); for (int i = clsList.size() - 1; i >= 0; i--) { cls = getExactClass(((JCClass)clsList.get(i)).getFullName()); if (cls != null) { boolean difPkg = !cls.getPackageName().equals(pkgName); List outerList = (i == 0 && inspectOuterClasses && cls.getName().indexOf('.') >= 0) ? getOuterClasses(cls) : null; int outerInd = (outerList != null) ? (outerList.size() - 1) : -1; do { if (outerInd >= 0) { cls = (JCClass)outerList.get(outerInd--); } JCMethod[] methods = cls.getMethods(); for (int j = 0; j < methods.length; j++) { JCMethod mtd = methods[j]; int mods = mtd.getModifiers(); if ((staticOnly && (mods & Modifier.STATIC) == 0) || (i > 0 && (mods & Modifier.PRIVATE) != 0) || (difPkg && (mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0) ) { continue; } if (exactMatch) { if (!mtd.getName().equals(name)) { continue; } } else { // match begining if (!mtd.getName().startsWith(name)) { continue; } } ts.add(mtd); } } while (outerInd >= 0); } } return new ArrayList(ts); } private List getClassList(JCClass cls) { JCFinder finder = JCompletion.getFinder(); cls = finder.getExactClass(cls.getFullName()); List ret; if (cls != null) { ret = cls.isInterface() ? JCUtilities.getAllInterfaces(cls) : JCUtilities.getSuperclasses(cls); ret.add(0, cls); } else { // class not found ret = new ArrayList(); // return empty list } return ret; } public String dumpClasses() { StringBuffer sb = new StringBuffer(8192); // expect huge growth JCClass[] ac = getAllClasses(); for (int i = 0; i < ac.length; i++) { sb.append(JCUtilities.dumpClass(ac[i])); sb.append("\n\n"); // NOI18N } return sb.toString(); } public static final class DefaultClassNameComparator implements Comparator { public int compare(Object o1, Object o2) { if (o1 == o2) { return 0; } return ((JCClass)o1).getName().compareTo(((JCClass)o2).getName()); } } } /* * Log * 18 Gandalf-post-FCS1.16.1.0 4/5/00 Miloslav Metelka fixed interfaces * tracking * 17 Gandalf 1.16 1/13/00 Miloslav Metelka Localization * 16 Gandalf 1.15 11/24/99 Miloslav Metelka * 15 Gandalf 1.14 11/14/99 Miloslav Metelka * 14 Gandalf 1.13 11/9/99 Miloslav Metelka * 13 Gandalf 1.12 11/8/99 Miloslav Metelka * 12 Gandalf 1.11 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 11 Gandalf 1.10 10/10/99 Miloslav Metelka * 10 Gandalf 1.9 9/30/99 Miloslav Metelka * 9 Gandalf 1.8 9/15/99 Miloslav Metelka * 8 Gandalf 1.7 8/18/99 Miloslav Metelka * 7 Gandalf 1.6 8/18/99 Miloslav Metelka * 6 Gandalf 1.5 8/9/99 Miloslav Metelka getExactPackage() patch * 5 Gandalf 1.4 7/30/99 Miloslav Metelka * 4 Gandalf 1.3 7/29/99 Miloslav Metelka * 3 Gandalf 1.2 7/22/99 Miloslav Metelka * 2 Gandalf 1.1 7/21/99 Miloslav Metelka * 1 Gandalf 1.0 7/20/99 Miloslav Metelka * $ */